package com.lipaluma.field.mapper.multiple;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.lipaluma.context.Context;
import com.lipaluma.exceptions.CsvParserValidationException;
import com.lipaluma.field.adaptor.Adaptor;
import com.lipaluma.field.adaptor.AdaptorFactory;
import com.lipaluma.field.adaptor.ChainAdaptor;
import com.lipaluma.field.mapper.FieldAdaptedOnEachValue;
import com.lipaluma.field.mapper.FieldMapper;
import com.lipaluma.field.mapper.FieldValidated;
import com.lipaluma.field.mapper.mapping.FieldMappingInfo;
import com.lipaluma.field.validator.ChainValidator;
import com.lipaluma.field.validator.ValidationError;
import com.lipaluma.field.validator.Validator;
import com.lipaluma.xml.mapping.XmlFieldMapping;

public abstract class AbstractStandardMultipleFieldMapper extends AbstractMultipleFieldMapper implements FieldAdaptedOnEachValue, FieldValidated {

	protected ChainAdaptor eachValueAdaptor;
	protected ChainValidator validator;

	public AbstractStandardMultipleFieldMapper() {}
	public AbstractStandardMultipleFieldMapper(List<FieldMappingInfo> mappings, boolean keepAll) {
		super(mappings, keepAll);
	}

	public AbstractStandardMultipleFieldMapper(List<FieldMappingInfo> mappings) {
		super(mappings);
	}

	@Override
	public void addNewAdaptorOnEachValue(Adaptor adaptor) {
		eachValueAdaptor.push(adaptor);
	}

	@Override
	public void initAdaptorOnEachValue(Context context, Field field) {
		this.eachValueAdaptor = AdaptorFactory.createNewChainAdaptor(context, field);
	}
	
	@Override
	public void initAdaptorOnEachValue(Context context, Field field, XmlFieldMapping mapping) {
		this.eachValueAdaptor = AdaptorFactory.createNewChainAdaptor(context, field, mapping);
	}
	
	@Override
	public void initValidator(Context context, Field field) {
		this.validator = createChainValidator(context, field);
		if(this.validator == null)
			this.validator = new ChainValidator(false);
	}

	@Override
	public void required(boolean required) {
		validator.setRequired(true);
	}
	/**
	 * init the Validator using annotations
	 * @param context TODO
	 * @param field field of the target object
	 * 
	 * @return the validator to be used for the {@link FieldMapper}
	 */
	protected abstract ChainValidator createChainValidator(Context context, Field field);

	@Override
	public void initValidator(Context context, Field field, XmlFieldMapping mapping) {
		this.validator = createChainValidator(context, field, mapping);
		if(this.validator == null)
			this.validator = new ChainValidator(false);
	}
	
	/**
	 * init the Validator using xml mapping
	 * @param context TODO
	 * @param field field of the target object
	 * 
	 * @return the validator to be used for the {@link FieldMapper}
	 */
	protected abstract ChainValidator createChainValidator(Context context, Field field, XmlFieldMapping mapping);

	@Override
	public List<String> adaptEachValues(List<String> values) {
		List<String> adaptedValues = new ArrayList<String>();
		for (String value : values) {
			if(StringUtils.isNotEmpty(value)) {
				String adaptValue = this.eachValueAdaptor.adapt(value);
				if(StringUtils.isNotEmpty(adaptValue) || keepAll)
					adaptedValues.add(adaptValue);
			} else if (keepAll)
				adaptedValues.add(value);
		}
		return adaptedValues;
	}

	@Override
	public void addNewValidator(Validator validator) {
		this.validator.push(validator);		
	}
	
	@Override
	public void validateValue(Object value) throws CsvParserValidationException {
		if(validator != null) {
			List<ValidationError> errors = new ArrayList<ValidationError>();
			if(!validator.validate(value, errors)) {
				StringBuilder message = new StringBuilder(getDescription());
				message.append("An error occured on validation of the value ").append(value).append("\n");
				for (ValidationError error : errors) {
					message.append(error.getMessage()).append("\n");
				}
				throw new CsvParserValidationException(message.toString());
			}
		}
	}
}
